home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.002 / stk-3 / STk-3.1 / Tk / generic / tkScrollbar.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-31  |  39.6 KB  |  1,255 lines

  1. /* 
  2.  * tkScrollbar.c --
  3.  *
  4.  *    This module implements a scrollbar widgets for the Tk
  5.  *    toolkit.  A scrollbar displays a slider and two arrows;
  6.  *    mouse clicks on features within the scrollbar cause
  7.  *    scrolling commands to be invoked.
  8.  *
  9.  * Copyright (c) 1990-1994 The Regents of the University of California.
  10.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  11.  *
  12.  * See the file "license.terms" for information on usage and redistribution
  13.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  14.  *
  15.  * SCCS: @(#) tkScrollbar.c 1.79 96/02/15 18:52:40
  16.  */
  17.  
  18. #include "tkPort.h"
  19. #include "default.h"
  20. #include "tkInt.h"
  21.  
  22. /*
  23.  * A data structure of the following type is kept for each scrollbar
  24.  * widget managed by this file:
  25.  */
  26.  
  27. typedef struct {
  28.     Tk_Window tkwin;        /* Window that embodies the scrollbar.  NULL
  29.                  * means that the window has been destroyed
  30.                  * but the data structures haven't yet been
  31.                  * cleaned up.*/
  32.     Display *display;        /* Display containing widget.  Used, among
  33.                  * other things, so that resources can be
  34.                  * freed even after tkwin has gone away. */
  35.     Tcl_Interp *interp;        /* Interpreter associated with scrollbar. */
  36.     Tcl_Command widgetCmd;    /* Token for scrollbar's widget command. */
  37.     Tk_Uid orientUid;        /* Orientation for window ("vertical" or
  38.                  * "horizontal"). */
  39.     int vertical;        /* Non-zero means vertical orientation
  40.                  * requested, zero means horizontal. */
  41.     int width;            /* Desired narrow dimension of scrollbar,
  42.                  * in pixels. */
  43.     char *command;        /* Command prefix to use when invoking
  44.                  * scrolling commands.  NULL means don't
  45.                  * invoke commands.  Malloc'ed. */
  46.     int commandSize;        /* Number of non-NULL bytes in command. */
  47.     int repeatDelay;        /* How long to wait before auto-repeating
  48.                  * on scrolling actions (in ms). */
  49.     int repeatInterval;        /* Interval between autorepeats (in ms). */
  50.     int jump;            /* Value of -jump option. */
  51.  
  52.     /*
  53.      * Information used when displaying widget:
  54.      */
  55.  
  56.     int borderWidth;        /* Width of 3-D borders. */
  57.     Tk_3DBorder bgBorder;    /* Used for drawing background (all flat
  58.                  * surfaces except for trough). */
  59.     Tk_3DBorder activeBorder;    /* For drawing backgrounds when active (i.e.
  60.                  * when mouse is positioned over element). */
  61.     XColor *troughColorPtr;    /* Color for drawing trough. */
  62.     GC troughGC;        /* For drawing trough. */
  63.     GC copyGC;            /* Used for copying from pixmap onto screen. */
  64.     int relief;            /* Indicates whether window as a whole is
  65.                  * raised, sunken, or flat. */
  66.     int highlightWidth;        /* Width in pixels of highlight to draw
  67.                  * around widget when it has the focus.
  68.                  * <= 0 means don't draw a highlight. */
  69.     XColor *highlightBgColorPtr;
  70.                 /* Color for drawing traversal highlight
  71.                  * area when highlight is off. */
  72.     XColor *highlightColorPtr;    /* Color for drawing traversal highlight. */
  73.     int inset;            /* Total width of all borders, including
  74.                  * traversal highlight and 3-D border.
  75.                  * Indicates how much interior stuff must
  76.                  * be offset from outside edges to leave
  77.                  * room for borders. */
  78.     int elementBorderWidth;    /* Width of border to draw around elements
  79.                  * inside scrollbar (arrows and slider).
  80.                  * -1 means use borderWidth. */
  81.     int arrowLength;        /* Length of arrows along long dimension of
  82.                  * scrollbar, including space for a small gap
  83.                  * between the arrow and the slider.
  84.                  * Recomputed on window size changes. */
  85.     int sliderFirst;        /* Pixel coordinate of top or left edge
  86.                  * of slider area, including border. */
  87.     int sliderLast;        /* Coordinate of pixel just after bottom
  88.                  * or right edge of slider area, including
  89.                  * border. */
  90.     int activeField;        /* Names field to be displayed in active
  91.                  * colors, such as TOP_ARROW, or 0 for
  92.                  * no field. */
  93.     int activeRelief;        /* Value of -activeRelief option: relief
  94.                  * to use for active element. */
  95.  
  96.     /*
  97.      * Information describing the application related to the scrollbar.
  98.      * This information is provided by the application by invoking the
  99.      * "set" widget command.  This information can now be provided in
  100.      * two ways:  the "old" form (totalUnits, windowUnits, firstUnit,
  101.      * and lastUnit), or the "new" form (firstFraction and lastFraction).
  102.      * FirstFraction and lastFraction will always be valid, but
  103.      * the old-style information is only valid if the NEW_STYLE_COMMANDS
  104.      * flag is 0.
  105.      */
  106.  
  107.     int totalUnits;        /* Total dimension of application, in
  108.                  * units.  Valid only if the NEW_STYLE_COMMANDS
  109.                  * flag isn't set. */
  110.     int windowUnits;        /* Maximum number of units that can be
  111.                  * displayed in the window at once.  Valid
  112.                  * only if the NEW_STYLE_COMMANDS flag isn't
  113.                  * set. */
  114.     int firstUnit;        /* Number of last unit visible in
  115.                  * application's window.  Valid only if the
  116.                  * NEW_STYLE_COMMANDS flag isn't set. */
  117.     int lastUnit;        /* Index of last unit visible in window.
  118.                  * Valid only if the NEW_STYLE_COMMANDS
  119.                  * flag isn't set. */
  120.     double firstFraction;    /* Position of first visible thing in window,
  121.                  * specified as a fraction between 0 and
  122.                  * 1.0. */
  123.     double lastFraction;    /* Position of last visible thing in window,
  124.                  * specified as a fraction between 0 and
  125.                  * 1.0. */
  126.  
  127.     /*
  128.      * Miscellaneous information:
  129.      */
  130.  
  131.     Tk_Cursor cursor;        /* Current cursor for window, or None. */
  132.     char *takeFocus;        /* Value of -takefocus option;  not used in
  133.                  * the C code, but used by keyboard traversal
  134.                  * scripts.  Malloc'ed, but may be NULL. */
  135.     int flags;            /* Various flags;  see below for
  136.                  * definitions. */
  137. } Scrollbar;
  138.  
  139. /*
  140.  * Legal values for "activeField" field of Scrollbar structures.  These
  141.  * are also the return values from the ScrollbarPosition procedure.
  142.  */
  143.  
  144. #define OUTSIDE        0
  145. #define TOP_ARROW    1
  146. #define TOP_GAP        2
  147. #define SLIDER        3
  148. #define BOTTOM_GAP    4
  149. #define BOTTOM_ARROW    5
  150.  
  151. /*
  152.  * Flag bits for scrollbars:
  153.  * 
  154.  * REDRAW_PENDING:        Non-zero means a DoWhenIdle handler
  155.  *                has already been queued to redraw
  156.  *                this window.
  157.  * NEW_STYLE_COMMANDS:        Non-zero means the new style of commands
  158.  *                should be used to communicate with the
  159.  *                widget:  ".t yview scroll 2 lines", instead
  160.  *                of ".t yview 40", for example.
  161.  * GOT_FOCUS:            Non-zero means this window has the input
  162.  *                focus.
  163.  */
  164.  
  165. #define REDRAW_PENDING        1
  166. #define NEW_STYLE_COMMANDS    2
  167. #define GOT_FOCUS        4
  168.  
  169. /*
  170.  * Minimum slider length, in pixels (designed to make sure that the slider
  171.  * is always easy to grab with the mouse).
  172.  */
  173.  
  174. #define MIN_SLIDER_LENGTH    5
  175.  
  176. /*
  177.  * Information used for argv parsing.
  178.  */
  179.  
  180. static Tk_ConfigSpec configSpecs[] = {
  181.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  182.     DEF_SCROLLBAR_ACTIVE_BG_COLOR, Tk_Offset(Scrollbar, activeBorder),
  183.     TK_CONFIG_COLOR_ONLY},
  184.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  185.     DEF_SCROLLBAR_ACTIVE_BG_MONO, Tk_Offset(Scrollbar, activeBorder),
  186.     TK_CONFIG_MONO_ONLY},
  187.     {TK_CONFIG_RELIEF, "-activerelief", "activeRelief", "Relief",
  188.     DEF_SCROLLBAR_ACTIVE_RELIEF, Tk_Offset(Scrollbar, activeRelief), 0},
  189.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  190.     DEF_SCROLLBAR_BG_COLOR, Tk_Offset(Scrollbar, bgBorder),
  191.     TK_CONFIG_COLOR_ONLY},
  192.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  193.     DEF_SCROLLBAR_BG_MONO, Tk_Offset(Scrollbar, bgBorder),
  194.     TK_CONFIG_MONO_ONLY},
  195.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  196.     (char *) NULL, 0, 0},
  197.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  198.     (char *) NULL, 0, 0},
  199.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  200.     DEF_SCROLLBAR_BORDER_WIDTH, Tk_Offset(Scrollbar, borderWidth), 0},
  201. #ifdef STk_CODE
  202.     {TK_CONFIG_CLOSURE, "-command", "command", "Command",
  203. #else
  204.     {TK_CONFIG_STRING, "-command", "command", "Command",
  205. #endif
  206.     DEF_SCROLLBAR_COMMAND, Tk_Offset(Scrollbar, command),
  207.     TK_CONFIG_NULL_OK},
  208.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  209.     DEF_SCROLLBAR_CURSOR, Tk_Offset(Scrollbar, cursor), TK_CONFIG_NULL_OK},
  210.     {TK_CONFIG_PIXELS, "-elementborderwidth", "elementBorderWidth",
  211.     "BorderWidth", DEF_SCROLLBAR_EL_BORDER_WIDTH,
  212.     Tk_Offset(Scrollbar, elementBorderWidth), 0},
  213.     {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
  214.     "HighlightBackground", DEF_SCROLLBAR_HIGHLIGHT_BG,
  215.     Tk_Offset(Scrollbar, highlightBgColorPtr), 0},
  216.     {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
  217.     DEF_SCROLLBAR_HIGHLIGHT,
  218.     Tk_Offset(Scrollbar, highlightColorPtr), 0},
  219.     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
  220.     "HighlightThickness",
  221.     DEF_SCROLLBAR_HIGHLIGHT_WIDTH, Tk_Offset(Scrollbar, highlightWidth), 0},
  222.     {TK_CONFIG_BOOLEAN, "-jump", "jump", "Jump",
  223.     DEF_SCROLLBAR_JUMP, Tk_Offset(Scrollbar, jump), 0},
  224.     {TK_CONFIG_UID, "-orient", "orient", "Orient",
  225.     DEF_SCROLLBAR_ORIENT, Tk_Offset(Scrollbar, orientUid), 0},
  226.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  227.     DEF_SCROLLBAR_RELIEF, Tk_Offset(Scrollbar, relief), 0},
  228.     {TK_CONFIG_INT, "-repeatdelay", "repeatDelay", "RepeatDelay",
  229.     DEF_SCROLLBAR_REPEAT_DELAY, Tk_Offset(Scrollbar, repeatDelay), 0},
  230.     {TK_CONFIG_INT, "-repeatinterval", "repeatInterval", "RepeatInterval",
  231.     DEF_SCROLLBAR_REPEAT_INTERVAL, Tk_Offset(Scrollbar, repeatInterval), 0},
  232. #ifdef STk_CODE
  233.     {TK_CONFIG_CLOSURE, "-takefocus", "takeFocus", "TakeFocus",
  234. #else
  235.     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
  236. #endif
  237.     DEF_SCROLLBAR_TAKE_FOCUS, Tk_Offset(Scrollbar, takeFocus),
  238.     TK_CONFIG_NULL_OK},
  239.     {TK_CONFIG_COLOR, "-troughcolor", "troughColor", "Background",
  240.     DEF_SCROLLBAR_TROUGH_COLOR, Tk_Offset(Scrollbar, troughColorPtr),
  241.     TK_CONFIG_COLOR_ONLY},
  242.     {TK_CONFIG_COLOR, "-troughcolor", "troughColor", "Background",
  243.     DEF_SCROLLBAR_TROUGH_MONO, Tk_Offset(Scrollbar, troughColorPtr),
  244.     TK_CONFIG_MONO_ONLY},
  245.     {TK_CONFIG_PIXELS, "-width", "width", "Width",
  246.     DEF_SCROLLBAR_WIDTH, Tk_Offset(Scrollbar, width), 0},
  247.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  248.     (char *) NULL, 0, 0}
  249. };
  250.  
  251. /*
  252.  * Forward declarations for procedures defined later in this file:
  253.  */
  254.  
  255. static void        ComputeScrollbarGeometry _ANSI_ARGS_((
  256.                 Scrollbar *scrollPtr));
  257. static int        ConfigureScrollbar _ANSI_ARGS_((Tcl_Interp *interp,
  258.                 Scrollbar *scrollPtr, int argc, char **argv,
  259.                 int flags));
  260. static void        DestroyScrollbar _ANSI_ARGS_((char *memPtr));
  261. static void        DisplayScrollbar _ANSI_ARGS_((ClientData clientData));
  262. static void        EventuallyRedraw _ANSI_ARGS_((Scrollbar *scrollPtr));
  263. static void        ScrollbarCmdDeletedProc _ANSI_ARGS_((
  264.                 ClientData clientData));
  265. static void        ScrollbarEventProc _ANSI_ARGS_((ClientData clientData,
  266.                 XEvent *eventPtr));
  267. static int        ScrollbarPosition _ANSI_ARGS_((Scrollbar *scrollPtr,
  268.                 int x, int y));
  269. static int        ScrollbarWidgetCmd _ANSI_ARGS_((ClientData clientData,
  270.                 Tcl_Interp *, int argc, char **argv));
  271.  
  272. /*
  273.  *--------------------------------------------------------------
  274.  *
  275.  * Tk_ScrollbarCmd --
  276.  *
  277.  *    This procedure is invoked to process the "scrollbar" Tcl
  278.  *    command.  See the user documentation for details on what
  279.  *    it does.
  280.  *
  281.  * Results:
  282.  *    A standard Tcl result.
  283.  *
  284.  * Side effects:
  285.  *    See the user documentation.
  286.  *
  287.  *--------------------------------------------------------------
  288.  */
  289.  
  290. int
  291. Tk_ScrollbarCmd(clientData, interp, argc, argv)
  292.     ClientData clientData;    /* Main window associated with
  293.                  * interpreter. */
  294.     Tcl_Interp *interp;        /* Current interpreter. */
  295.     int argc;            /* Number of arguments. */
  296.     char **argv;        /* Argument strings. */
  297. {
  298.     Tk_Window tkwin = (Tk_Window) clientData;
  299.     register Scrollbar *scrollPtr;
  300.     Tk_Window new;
  301.  
  302.     if (argc < 2) {
  303.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  304.         argv[0], " pathName ?options?\"", (char *) NULL);
  305.     return TCL_ERROR;
  306.     }
  307.  
  308.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  309.     if (new == NULL) {
  310.     return TCL_ERROR;
  311.     }
  312.  
  313.     /*
  314.      * Initialize fields that won't be initialized by ConfigureScrollbar,
  315.      * or which ConfigureScrollbar expects to have reasonable values
  316.      * (e.g. resource pointers).
  317.      */
  318.  
  319.     scrollPtr = (Scrollbar *) ckalloc(sizeof(Scrollbar));
  320.     scrollPtr->tkwin = new;
  321.     scrollPtr->display = Tk_Display(new);
  322.     scrollPtr->interp = interp;
  323.     scrollPtr->widgetCmd = Tcl_CreateCommand(interp,
  324.         Tk_PathName(scrollPtr->tkwin), ScrollbarWidgetCmd,
  325.         (ClientData) scrollPtr, ScrollbarCmdDeletedProc);
  326.     scrollPtr->orientUid = NULL;
  327.     scrollPtr->vertical = 0;
  328.     scrollPtr->width = 0;
  329.     scrollPtr->command = NULL;
  330.     scrollPtr->commandSize = 0;
  331.     scrollPtr->repeatDelay = 0;
  332.     scrollPtr->repeatInterval = 0;
  333.     scrollPtr->borderWidth = 0;
  334.     scrollPtr->bgBorder = NULL;
  335.     scrollPtr->activeBorder = NULL;
  336.     scrollPtr->troughColorPtr = NULL;
  337.     scrollPtr->troughGC = None;
  338.     scrollPtr->copyGC = None;
  339.     scrollPtr->relief = TK_RELIEF_FLAT;
  340.     scrollPtr->highlightWidth = 0;
  341.     scrollPtr->highlightBgColorPtr = NULL;
  342.     scrollPtr->highlightColorPtr = NULL;
  343.     scrollPtr->inset = 0;
  344.     scrollPtr->elementBorderWidth = -1;
  345.     scrollPtr->arrowLength = 0;
  346.     scrollPtr->sliderFirst = 0;
  347.     scrollPtr->sliderLast = 0;
  348.     scrollPtr->activeField = 0;
  349.     scrollPtr->activeRelief = TK_RELIEF_RAISED;
  350.     scrollPtr->totalUnits = 0;
  351.     scrollPtr->windowUnits = 0;
  352.     scrollPtr->firstUnit = 0;
  353.     scrollPtr->lastUnit = 0;
  354.     scrollPtr->firstFraction = 0.0;
  355.     scrollPtr->lastFraction = 0.0;
  356.     scrollPtr->cursor = None;
  357.     scrollPtr->takeFocus = NULL;
  358.     scrollPtr->flags = 0;
  359.  
  360.     Tk_SetClass(scrollPtr->tkwin, "Scrollbar");
  361.     Tk_CreateEventHandler(scrollPtr->tkwin,
  362.         ExposureMask|StructureNotifyMask|FocusChangeMask,
  363.         ScrollbarEventProc, (ClientData) scrollPtr);
  364.     if (ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2, 0) != TCL_OK) {
  365.     goto error;
  366.     }
  367.  
  368. #ifdef STk_CODE
  369.     STk_sharp_dot_result(interp, Tk_PathName(scrollPtr->tkwin));
  370. #else
  371.     interp->result = Tk_PathName(scrollPtr->tkwin);
  372. #endif
  373.     return TCL_OK;
  374.  
  375.     error:
  376.     Tk_DestroyWindow(scrollPtr->tkwin);
  377.     return TCL_ERROR;
  378. }
  379.  
  380. /*
  381.  *--------------------------------------------------------------
  382.  *
  383.  * ScrollbarWidgetCmd --
  384.  *
  385.  *    This procedure is invoked to process the Tcl command
  386.  *    that corresponds to a widget managed by this module.
  387.  *    See the user documentation for details on what it does.
  388.  *
  389.  * Results:
  390.  *    A standard Tcl result.
  391.  *
  392.  * Side effects:
  393.  *    See the user documentation.
  394.  *
  395.  *--------------------------------------------------------------
  396.  */
  397.  
  398. static int
  399. ScrollbarWidgetCmd(clientData, interp, argc, argv)
  400.     ClientData clientData;    /* Information about scrollbar
  401.                      * widget. */
  402.     Tcl_Interp *interp;            /* Current interpreter. */
  403.     int argc;                /* Number of arguments. */
  404.     char **argv;            /* Argument strings. */
  405. {
  406.     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
  407.     int result = TCL_OK;
  408.     size_t length;
  409.     int c;
  410.  
  411.     if (argc < 2) {
  412.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  413.         argv[0], " option ?arg arg ...?\"", (char *) NULL);
  414.     return TCL_ERROR;
  415.     }
  416.     Tcl_Preserve((ClientData) scrollPtr);
  417.     c = argv[1][0];
  418.     length = strlen(argv[1]);
  419.     if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)) {
  420.     if (argc == 2) {
  421.         switch (scrollPtr->activeField) {
  422. #ifdef STk_CODE
  423.         case TOP_ARROW:        interp->result = "\"arrow1\"";    break;
  424.         case SLIDER:        interp->result = "\"slider\"";    break;
  425.         case BOTTOM_ARROW:    interp->result = "\"arrow2\"";    break;
  426.             default:        interp->result = "#f";
  427. #else
  428.         case TOP_ARROW:        interp->result = "arrow1";    break;
  429.         case SLIDER:        interp->result = "slider";    break;
  430.         case BOTTOM_ARROW:    interp->result = "arrow2";    break;
  431. #endif
  432.         }
  433.         goto done;
  434.     }
  435.     if (argc != 3) {
  436.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  437.             argv[0], " activate element\"", (char *) NULL);
  438.         goto error;
  439.     }
  440.     c = argv[2][0];
  441.     length = strlen(argv[2]);
  442.     if ((c == 'a') && (strcmp(argv[2], "arrow1") == 0)) {
  443.         scrollPtr->activeField = TOP_ARROW;
  444.     } else if ((c == 'a') && (strcmp(argv[2], "arrow2") == 0)) {
  445.         scrollPtr->activeField = BOTTOM_ARROW;
  446.     } else if ((c == 's') && (strncmp(argv[2], "slider", length) == 0)) {
  447.         scrollPtr->activeField = SLIDER;
  448.     } else {
  449.         scrollPtr->activeField = OUTSIDE;
  450.     }
  451.     EventuallyRedraw(scrollPtr);
  452.     } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
  453.         && (length >= 2)) {
  454.     if (argc != 3) {
  455.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  456.             argv[0], " cget option\"",
  457.             (char *) NULL);
  458.         goto error;
  459.     }
  460.     result = Tk_ConfigureValue(interp, scrollPtr->tkwin, configSpecs,
  461.         (char *) scrollPtr, argv[2], 0);
  462.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  463.         && (length >= 2)) {
  464.     if (argc == 2) {
  465.         result = Tk_ConfigureInfo(interp, scrollPtr->tkwin, configSpecs,
  466.             (char *) scrollPtr, (char *) NULL, 0);
  467.     } else if (argc == 3) {
  468.         result = Tk_ConfigureInfo(interp, scrollPtr->tkwin, configSpecs,
  469.             (char *) scrollPtr, argv[2], 0);
  470.     } else {
  471.         result = ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2,
  472.             TK_CONFIG_ARGV_ONLY);
  473.     }
  474.     } else if ((c == 'd') && (strncmp(argv[1], "delta", length) == 0)) {
  475.     int xDelta, yDelta, pixels, length;
  476.     double fraction;
  477.  
  478.     if (argc != 4) {
  479.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  480.             argv[0], " delta xDelta yDelta\"", (char *) NULL);
  481.         goto error;
  482.     }
  483.     if ((Tcl_GetInt(interp, argv[2], &xDelta) != TCL_OK)
  484.         || (Tcl_GetInt(interp, argv[3], &yDelta) != TCL_OK)) {
  485.         goto error;
  486.     }
  487.     if (scrollPtr->vertical) {
  488.         pixels = yDelta;
  489.         length = Tk_Height(scrollPtr->tkwin) - 1
  490.             - 2*(scrollPtr->arrowLength + scrollPtr->inset);
  491.     } else {
  492.         pixels = xDelta;
  493.         length = Tk_Width(scrollPtr->tkwin) - 1
  494.             - 2*(scrollPtr->arrowLength + scrollPtr->inset);
  495.     }
  496.     if (length == 0) {
  497.         fraction = 0.0;
  498.     } else {
  499.         fraction = ((double) pixels / (double) length);
  500.     }
  501.     sprintf(interp->result, "%g", fraction);
  502.     } else if ((c == 'f') && (strncmp(argv[1], "fraction", length) == 0)) {
  503.     int x, y, pos, length;
  504.     double fraction;
  505.  
  506.     if (argc != 4) {
  507.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  508.             argv[0], " fraction x y\"", (char *) NULL);
  509.         goto error;
  510.     }
  511.     if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
  512.         || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
  513.         goto error;
  514.     }
  515.     if (scrollPtr->vertical) {
  516.         pos = y - (scrollPtr->arrowLength + scrollPtr->inset);
  517.         length = Tk_Height(scrollPtr->tkwin) - 1
  518.             - 2*(scrollPtr->arrowLength + scrollPtr->inset);
  519.     } else {
  520.         pos = x - (scrollPtr->arrowLength + scrollPtr->inset);
  521.         length = Tk_Width(scrollPtr->tkwin) - 1
  522.             - 2*(scrollPtr->arrowLength + scrollPtr->inset);
  523.     }
  524.     if (length == 0) {
  525.         fraction = 0.0;
  526.     } else {
  527.         fraction = ((double) pos / (double) length);
  528.     }
  529.     if (fraction < 0) {
  530.         fraction = 0;
  531.     } else if (fraction > 1.0) {
  532.         fraction = 1.0;
  533.     }
  534.     sprintf(interp->result, "%g", fraction);
  535.     } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
  536.     if (argc != 2) {
  537.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  538.             argv[0], " get\"", (char *) NULL);
  539.         goto error;
  540.     }
  541.     if (scrollPtr->flags & NEW_STYLE_COMMANDS) {
  542.         char first[TCL_DOUBLE_SPACE], last[TCL_DOUBLE_SPACE];
  543.  
  544.         Tcl_PrintDouble(interp, scrollPtr->firstFraction, first);
  545.         Tcl_PrintDouble(interp, scrollPtr->lastFraction, last);
  546.         Tcl_AppendResult(interp, first, " ", last, (char *) NULL);
  547.     } else {
  548.         sprintf(interp->result, "%d %d %d %d", scrollPtr->totalUnits,
  549.             scrollPtr->windowUnits, scrollPtr->firstUnit,
  550.             scrollPtr->lastUnit);
  551.     }
  552.     } else if ((c == 'i') && (strncmp(argv[1], "identify", length) == 0)) {
  553.     int x, y, thing;
  554.  
  555.     if (argc != 4) {
  556.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  557.             argv[0], " identify x y\"", (char *) NULL);
  558.         goto error;
  559.     }
  560.     if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
  561.         || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
  562.         goto error;
  563.     }
  564.     thing = ScrollbarPosition(scrollPtr, x,y);
  565. #ifdef STk_CODE
  566.     switch (thing) {
  567.         case TOP_ARROW:    interp->result = "\"arrow1\"";    break;
  568.         case TOP_GAP:    interp->result = "\"trough1\"";    break;
  569.         case SLIDER:    interp->result = "\"slider\"";    break;
  570.         case BOTTOM_GAP:    interp->result = "\"trough2\"";    break;
  571.         case BOTTOM_ARROW:    interp->result = "\"arrow2\"";    break;
  572.         default:        interp->result = "#f";
  573.     }
  574. #else
  575.     switch (thing) {
  576.         case TOP_ARROW:    interp->result = "arrow1";    break;
  577.         case TOP_GAP:    interp->result = "trough1";    break;
  578.         case SLIDER:    interp->result = "slider";    break;
  579.         case BOTTOM_GAP:    interp->result = "trough2";    break;
  580.         case BOTTOM_ARROW:    interp->result = "arrow2";    break;
  581.     }
  582. #endif
  583.     } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) {
  584.     int totalUnits, windowUnits, firstUnit, lastUnit;
  585.  
  586.     if (argc == 4) {
  587.         double first, last;
  588.  
  589.         if (Tcl_GetDouble(interp, argv[2], &first) != TCL_OK) {
  590.         goto error;
  591.         }
  592.         if (Tcl_GetDouble(interp, argv[3], &last) != TCL_OK) {
  593.         goto error;
  594.         }
  595.         if (first < 0) {
  596.         scrollPtr->firstFraction = 0;
  597.         } else if (first > 1.0) {
  598.         scrollPtr->firstFraction = 1.0;
  599.         } else {
  600.         scrollPtr->firstFraction = first;
  601.         }
  602.         if (last < scrollPtr->firstFraction) {
  603.         scrollPtr->lastFraction = scrollPtr->firstFraction;
  604.         } else if (last > 1.0) {
  605.         scrollPtr->lastFraction = 1.0;
  606.         } else {
  607.         scrollPtr->lastFraction = last;
  608.         }
  609.         scrollPtr->flags |= NEW_STYLE_COMMANDS;
  610.     } else if (argc == 6) {
  611.         if (Tcl_GetInt(interp, argv[2], &totalUnits) != TCL_OK) {
  612.         goto error;
  613.         }
  614.         if (totalUnits < 0) {
  615.         totalUnits = 0;
  616.         }
  617.         if (Tcl_GetInt(interp, argv[3], &windowUnits) != TCL_OK) {
  618.         goto error;
  619.         }
  620.         if (windowUnits < 0) {
  621.         windowUnits = 0;
  622.         }
  623.         if (Tcl_GetInt(interp, argv[4], &firstUnit) != TCL_OK) {
  624.         goto error;
  625.         }
  626.         if (Tcl_GetInt(interp, argv[5], &lastUnit) != TCL_OK) {
  627.         goto error;
  628.         }
  629.         if (totalUnits > 0) {
  630.         if (lastUnit < firstUnit) {
  631.             lastUnit = firstUnit;
  632.         }
  633.         } else {
  634.         firstUnit = lastUnit = 0;
  635.         }
  636.         scrollPtr->totalUnits = totalUnits;
  637.         scrollPtr->windowUnits = windowUnits;
  638.         scrollPtr->firstUnit = firstUnit;
  639.         scrollPtr->lastUnit = lastUnit;
  640.         if (scrollPtr->totalUnits == 0) {
  641.         scrollPtr->firstFraction = 0.0;
  642.         scrollPtr->lastFraction = 1.0;
  643.         } else {
  644.         scrollPtr->firstFraction = ((double) firstUnit)/totalUnits;
  645.         scrollPtr->lastFraction = ((double) (lastUnit+1))/totalUnits;
  646.         }
  647.         scrollPtr->flags &= ~NEW_STYLE_COMMANDS;
  648.     } else {
  649.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  650.             argv[0], " set firstFraction lastFraction\" or \"",
  651.             argv[0],
  652.             " set totalUnits windowUnits firstUnit lastUnit\"",
  653.             (char *) NULL);
  654.         goto error;
  655.     }
  656.     ComputeScrollbarGeometry(scrollPtr);
  657.     EventuallyRedraw(scrollPtr);
  658.     } else {
  659.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  660.         "\": must be activate, cget, configure, delta, fraction, ",
  661.         "get, identify, or set", (char *) NULL);
  662.     goto error;
  663.     }
  664.     done:
  665.     Tcl_Release((ClientData) scrollPtr);
  666.     return result;
  667.  
  668.     error:
  669.     Tcl_Release((ClientData) scrollPtr);
  670.     return TCL_ERROR;
  671. }
  672.  
  673. /*
  674.  *----------------------------------------------------------------------
  675.  *
  676.  * DestroyScrollbar --
  677.  *
  678.  *    This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
  679.  *    to clean up the internal structure of a scrollbar at a safe time
  680.  *    (when no-one is using it anymore).
  681.  *
  682.  * Results:
  683.  *    None.
  684.  *
  685.  * Side effects:
  686.  *    Everything associated with the scrollbar is freed up.
  687.  *
  688.  *----------------------------------------------------------------------
  689.  */
  690.  
  691. static void
  692. DestroyScrollbar(memPtr)
  693.     char *memPtr;    /* Info about scrollbar widget. */
  694. {
  695.     register Scrollbar *scrollPtr = (Scrollbar *) memPtr;
  696.  
  697.     /*
  698.      * Free up all the stuff that requires special handling, then
  699.      * let Tk_FreeOptions handle all the standard option-related
  700.      * stuff.
  701.      */
  702.  
  703.     if (scrollPtr->troughGC != None) {
  704.     Tk_FreeGC(scrollPtr->display, scrollPtr->troughGC);
  705.     }
  706.     if (scrollPtr->copyGC != None) {
  707.     Tk_FreeGC(scrollPtr->display, scrollPtr->copyGC);
  708.     }
  709.     Tk_FreeOptions(configSpecs, (char *) scrollPtr, scrollPtr->display, 0);
  710.     ckfree((char *) scrollPtr);
  711. }
  712.  
  713. /*
  714.  *----------------------------------------------------------------------
  715.  *
  716.  * ConfigureScrollbar --
  717.  *
  718.  *    This procedure is called to process an argv/argc list, plus
  719.  *    the Tk option database, in order to configure (or
  720.  *    reconfigure) a scrollbar widget.
  721.  *
  722.  * Results:
  723.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  724.  *    returned, then interp->result contains an error message.
  725.  *
  726.  * Side effects:
  727.  *    Configuration information, such as colors, border width,
  728.  *    etc. get set for scrollPtr;  old resources get freed,
  729.  *    if there were any.
  730.  *
  731.  *----------------------------------------------------------------------
  732.  */
  733.  
  734. static int
  735. ConfigureScrollbar(interp, scrollPtr, argc, argv, flags)
  736.     Tcl_Interp *interp;            /* Used for error reporting. */
  737.     register Scrollbar *scrollPtr;    /* Information about widget;  may or
  738.                      * may not already have values for
  739.                      * some fields. */
  740.     int argc;                /* Number of valid entries in argv. */
  741.     char **argv;            /* Arguments. */
  742.     int flags;                /* Flags to pass to
  743.                      * Tk_ConfigureWidget. */
  744. {
  745.     size_t length;
  746.     XGCValues gcValues;
  747.     GC new;
  748.  
  749.     if (Tk_ConfigureWidget(interp, scrollPtr->tkwin, configSpecs,
  750.         argc, argv, (char *) scrollPtr, flags) != TCL_OK) {
  751.     return TCL_ERROR;
  752.     }
  753.  
  754.     /*
  755.      * A few options need special processing, such as parsing the
  756.      * orientation or setting the background from a 3-D border.
  757.      */
  758.  
  759.     length = strlen(scrollPtr->orientUid);
  760.     if (strncmp(scrollPtr->orientUid, "vertical", length) == 0) {
  761.     scrollPtr->vertical = 1;
  762.     } else if (strncmp(scrollPtr->orientUid, "horizontal", length) == 0) {
  763.     scrollPtr->vertical = 0;
  764.     } else {
  765.     Tcl_AppendResult(interp, "bad orientation \"", scrollPtr->orientUid,
  766.         "\": must be vertical or horizontal", (char *) NULL);
  767.     return TCL_ERROR;
  768.     }
  769.  
  770.     if (scrollPtr->command != NULL) {
  771.     scrollPtr->commandSize = strlen(scrollPtr->command);
  772.     } else {
  773.     scrollPtr->commandSize = 0;
  774.     }
  775.  
  776.     Tk_SetBackgroundFromBorder(scrollPtr->tkwin, scrollPtr->bgBorder);
  777.  
  778.     gcValues.foreground = scrollPtr->troughColorPtr->pixel;
  779.     new = Tk_GetGC(scrollPtr->tkwin, GCForeground, &gcValues);
  780.     if (scrollPtr->troughGC != None) {
  781.     Tk_FreeGC(scrollPtr->display, scrollPtr->troughGC);
  782.     }
  783.     scrollPtr->troughGC = new;
  784.     if (scrollPtr->copyGC == None) {
  785.     gcValues.graphics_exposures = False;
  786.     scrollPtr->copyGC = Tk_GetGC(scrollPtr->tkwin, GCGraphicsExposures,
  787.         &gcValues);
  788.     }
  789.  
  790.     /*
  791.      * Register the desired geometry for the window (leave enough space
  792.      * for the two arrows plus a minimum-size slider, plus border around
  793.      * the whole window, if any).  Then arrange for the window to be
  794.      * redisplayed.
  795.      */
  796.  
  797.     ComputeScrollbarGeometry(scrollPtr);
  798.     EventuallyRedraw(scrollPtr);
  799.     return TCL_OK;
  800. }
  801.  
  802. /*
  803.  *--------------------------------------------------------------
  804.  *
  805.  * DisplayScrollbar --
  806.  *
  807.  *    This procedure redraws the contents of a scrollbar window.
  808.  *    It is invoked as a do-when-idle handler, so it only runs
  809.  *    when there's nothing else for the application to do.
  810.  *
  811.  * Results:
  812.  *    None.
  813.  *
  814.  * Side effects:
  815.  *    Information appears on the screen.
  816.  *
  817.  *--------------------------------------------------------------
  818.  */
  819.  
  820. static void
  821. DisplayScrollbar(clientData)
  822.     ClientData clientData;    /* Information about window. */
  823. {
  824.     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
  825.     register Tk_Window tkwin = scrollPtr->tkwin;
  826.     XPoint points[7];
  827.     Tk_3DBorder border;
  828.     int relief, width, elementBorderWidth;
  829.     Pixmap pixmap;
  830.  
  831.     if ((scrollPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  832.     goto done;
  833.     }
  834.  
  835.     if (scrollPtr->vertical) {
  836.     width = Tk_Width(tkwin) - 2*scrollPtr->inset;
  837.     } else {
  838.     width = Tk_Height(tkwin) - 2*scrollPtr->inset;
  839.     }
  840.     elementBorderWidth = scrollPtr->elementBorderWidth;
  841.     if (elementBorderWidth < 0) {
  842.     elementBorderWidth = scrollPtr->borderWidth;
  843.     }
  844.  
  845.     /*
  846.      * In order to avoid screen flashes, this procedure redraws
  847.      * the scrollbar in a pixmap, then copies the pixmap to the
  848.      * screen in a single operation.  This means that there's no
  849.      * point in time where the on-sreen image has been cleared.
  850.      */
  851.  
  852.     pixmap = Tk_GetPixmap(scrollPtr->display, Tk_WindowId(tkwin),
  853.         Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
  854.  
  855.     if (scrollPtr->highlightWidth != 0) {
  856.     GC gc;
  857.  
  858.     if (scrollPtr->flags & GOT_FOCUS) {
  859.         gc = Tk_GCForColor(scrollPtr->highlightColorPtr, pixmap);
  860.     } else {
  861.         gc = Tk_GCForColor(scrollPtr->highlightBgColorPtr, pixmap);
  862.     }
  863.     Tk_DrawFocusHighlight(tkwin, gc, scrollPtr->highlightWidth, pixmap);
  864.     }
  865.     Tk_Draw3DRectangle(tkwin, pixmap, scrollPtr->bgBorder,
  866.         scrollPtr->highlightWidth, scrollPtr->highlightWidth,
  867.         Tk_Width(tkwin) - 2*scrollPtr->highlightWidth,
  868.         Tk_Height(tkwin) - 2*scrollPtr->highlightWidth,
  869.         scrollPtr->borderWidth, scrollPtr->relief);
  870.     XFillRectangle(scrollPtr->display, pixmap, scrollPtr->troughGC,
  871.         scrollPtr->inset, scrollPtr->inset,
  872.         (unsigned) (Tk_Width(tkwin) - 2*scrollPtr->inset),
  873.         (unsigned) (Tk_Height(tkwin) - 2*scrollPtr->inset));
  874.  
  875.     /*
  876.      * Draw the top or left arrow.  The coordinates of the polygon
  877.      * points probably seem odd, but they were carefully chosen with
  878.      * respect to X's rules for filling polygons.  These point choices
  879.      * cause the arrows to just fill the narrow dimension of the
  880.      * scrollbar and be properly centered.
  881.      */
  882.  
  883.     if (scrollPtr->activeField == TOP_ARROW) {
  884.     border = scrollPtr->activeBorder;
  885.     relief = scrollPtr->activeField == TOP_ARROW ? scrollPtr->activeRelief
  886.         : TK_RELIEF_RAISED;
  887.     } else {
  888.     border = scrollPtr->bgBorder;
  889.     relief = TK_RELIEF_RAISED;
  890.     }
  891.     if (scrollPtr->vertical) {
  892.     points[0].x = scrollPtr->inset - 1;
  893.     points[0].y = scrollPtr->arrowLength + scrollPtr->inset - 1;
  894.     points[1].x = width + scrollPtr->inset;
  895.     points[1].y = points[0].y;
  896.     points[2].x = width/2 + scrollPtr->inset;
  897.     points[2].y = scrollPtr->inset - 1;
  898.     Tk_Fill3DPolygon(tkwin, pixmap, border, points, 3,
  899.         elementBorderWidth, relief);
  900.     } else {
  901.     points[0].x = scrollPtr->arrowLength + scrollPtr->inset - 1;
  902.     points[0].y = scrollPtr->inset - 1;
  903.     points[1].x = scrollPtr->inset;
  904.     points[1].y = width/2 + scrollPtr->inset;
  905.     points[2].x = points[0].x;
  906.     points[2].y = width + scrollPtr->inset;
  907.     Tk_Fill3DPolygon(tkwin, pixmap, border, points, 3,
  908.         elementBorderWidth, relief);
  909.     }
  910.  
  911.     /*
  912.      * Display the bottom or right arrow.
  913.      */
  914.  
  915.     if (scrollPtr->activeField == BOTTOM_ARROW) {
  916.     border = scrollPtr->activeBorder;
  917.     relief = scrollPtr->activeField == BOTTOM_ARROW
  918.         ? scrollPtr->activeRelief : TK_RELIEF_RAISED;
  919.     } else {
  920.     border = scrollPtr->bgBorder;
  921.     relief = TK_RELIEF_RAISED;
  922.     }
  923.     if (scrollPtr->vertical) {
  924.     points[0].x = scrollPtr->inset;
  925.     points[0].y = Tk_Height(tkwin) - scrollPtr->arrowLength
  926.         - scrollPtr->inset + 1;
  927.     points[1].x = width/2 + scrollPtr->inset;
  928.     points[1].y = Tk_Height(tkwin) - scrollPtr->inset;
  929.     points[2].x = width + scrollPtr->inset;
  930.     points[2].y = points[0].y;
  931.     Tk_Fill3DPolygon(tkwin, pixmap, border,
  932.         points, 3, elementBorderWidth, relief);
  933.     } else {
  934.     points[0].x = Tk_Width(tkwin) - scrollPtr->arrowLength
  935.         - scrollPtr->inset + 1;
  936.     points[0].y = scrollPtr->inset - 1;
  937.     points[1].x = points[0].x;
  938.     points[1].y = width + scrollPtr->inset;
  939.     points[2].x = Tk_Width(tkwin) - scrollPtr->inset;
  940.     points[2].y = width/2 + scrollPtr->inset;
  941.     Tk_Fill3DPolygon(tkwin, pixmap, border,
  942.         points, 3, elementBorderWidth, relief);
  943.     }
  944.  
  945.     /*
  946.      * Display the slider.
  947.      */
  948.  
  949.     if (scrollPtr->activeField == SLIDER) {
  950.     border = scrollPtr->activeBorder;
  951.     relief = scrollPtr->activeField == SLIDER ? scrollPtr->activeRelief
  952.         : TK_RELIEF_RAISED;
  953.     } else {
  954.     border = scrollPtr->bgBorder;
  955.     relief = TK_RELIEF_RAISED;
  956.     }
  957.     if (scrollPtr->vertical) {
  958.     Tk_Fill3DRectangle(tkwin, pixmap, border,
  959.         scrollPtr->inset, scrollPtr->sliderFirst,
  960.         width, scrollPtr->sliderLast - scrollPtr->sliderFirst,
  961.         elementBorderWidth, relief);
  962.     } else {
  963.     Tk_Fill3DRectangle(tkwin, pixmap, border,
  964.         scrollPtr->sliderFirst, scrollPtr->inset,
  965.         scrollPtr->sliderLast - scrollPtr->sliderFirst, width,
  966.         elementBorderWidth, relief);
  967.     }
  968.  
  969.     /*
  970.      * Copy the information from the off-screen pixmap onto the screen,
  971.      * then delete the pixmap.
  972.      */
  973.  
  974.     XCopyArea(scrollPtr->display, pixmap, Tk_WindowId(tkwin),
  975.         scrollPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin),
  976.         (unsigned) Tk_Height(tkwin), 0, 0);
  977.     Tk_FreePixmap(scrollPtr->display, pixmap);
  978.  
  979.     done:
  980.     scrollPtr->flags &= ~REDRAW_PENDING;
  981. }
  982.  
  983. /*
  984.  *--------------------------------------------------------------
  985.  *
  986.  * ScrollbarEventProc --
  987.  *
  988.  *    This procedure is invoked by the Tk dispatcher for various
  989.  *    events on scrollbars.
  990.  *
  991.  * Results:
  992.  *    None.
  993.  *
  994.  * Side effects:
  995.  *    When the window gets deleted, internal structures get
  996.  *    cleaned up.  When it gets exposed, it is redisplayed.
  997.  *
  998.  *--------------------------------------------------------------
  999.  */
  1000.  
  1001. static void
  1002. ScrollbarEventProc(clientData, eventPtr)
  1003.     ClientData clientData;    /* Information about window. */
  1004.     XEvent *eventPtr;        /* Information about event. */
  1005. {
  1006.     Scrollbar *scrollPtr = (Scrollbar *) clientData;
  1007.  
  1008.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  1009.     EventuallyRedraw(scrollPtr);
  1010.     } else if (eventPtr->type == DestroyNotify) {
  1011.     if (scrollPtr->tkwin != NULL) {
  1012.         scrollPtr->tkwin = NULL;
  1013.         Tcl_DeleteCommand(scrollPtr->interp,
  1014.             Tcl_GetCommandName(scrollPtr->interp,
  1015.             scrollPtr->widgetCmd));
  1016.     }
  1017.     if (scrollPtr->flags & REDRAW_PENDING) {
  1018.         Tcl_CancelIdleCall(DisplayScrollbar, (ClientData) scrollPtr);
  1019.     }
  1020.     Tcl_EventuallyFree((ClientData) scrollPtr, DestroyScrollbar);
  1021.     } else if (eventPtr->type == ConfigureNotify) {
  1022.     ComputeScrollbarGeometry(scrollPtr);
  1023.     EventuallyRedraw(scrollPtr);
  1024.     } else if (eventPtr->type == FocusIn) {
  1025.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1026.         scrollPtr->flags |= GOT_FOCUS;
  1027.         if (scrollPtr->highlightWidth > 0) {
  1028.         EventuallyRedraw(scrollPtr);
  1029.         }
  1030.     }
  1031.     } else if (eventPtr->type == FocusOut) {
  1032.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1033.         scrollPtr->flags &= ~GOT_FOCUS;
  1034.         if (scrollPtr->highlightWidth > 0) {
  1035.         EventuallyRedraw(scrollPtr);
  1036.         }
  1037.     }
  1038.     }
  1039. }
  1040.  
  1041. /*
  1042.  *----------------------------------------------------------------------
  1043.  *
  1044.  * ScrollbarCmdDeletedProc --
  1045.  *
  1046.  *    This procedure is invoked when a widget command is deleted.  If
  1047.  *    the widget isn't already in the process of being destroyed,
  1048.  *    this command destroys it.
  1049.  *
  1050.  * Results:
  1051.  *    None.
  1052.  *
  1053.  * Side effects:
  1054.  *    The widget is destroyed.
  1055.  *
  1056.  *----------------------------------------------------------------------
  1057.  */
  1058.  
  1059. static void
  1060. ScrollbarCmdDeletedProc(clientData)
  1061.     ClientData clientData;    /* Pointer to widget record for widget. */
  1062. {
  1063.     Scrollbar *scrollPtr = (Scrollbar *) clientData;
  1064.     Tk_Window tkwin = scrollPtr->tkwin;
  1065.  
  1066.     /*
  1067.      * This procedure could be invoked either because the window was
  1068.      * destroyed and the command was then deleted (in which case tkwin
  1069.      * is NULL) or because the command was deleted, and then this procedure
  1070.      * destroys the widget.
  1071.      */
  1072.  
  1073.     if (tkwin != NULL) {
  1074.     scrollPtr->tkwin = NULL;
  1075.     Tk_DestroyWindow(tkwin);
  1076.     }
  1077. }
  1078.  
  1079. /*
  1080.  *----------------------------------------------------------------------
  1081.  *
  1082.  * ComputeScrollbarGeometry --
  1083.  *
  1084.  *    After changes in a scrollbar's size or configuration, this
  1085.  *    procedure recomputes various geometry information used in
  1086.  *    displaying the scrollbar.
  1087.  *
  1088.  * Results:
  1089.  *    None.
  1090.  *
  1091.  * Side effects:
  1092.  *    The scrollbar will be displayed differently.
  1093.  *
  1094.  *----------------------------------------------------------------------
  1095.  */
  1096.  
  1097. static void
  1098. ComputeScrollbarGeometry(scrollPtr)
  1099.     register Scrollbar *scrollPtr;    /* Scrollbar whose geometry may
  1100.                      * have changed. */
  1101. {
  1102.     int width, fieldLength;
  1103.  
  1104.     if (scrollPtr->highlightWidth < 0) {
  1105.     scrollPtr->highlightWidth = 0;
  1106.     }
  1107.     scrollPtr->inset = scrollPtr->highlightWidth + scrollPtr->borderWidth;
  1108.     width = (scrollPtr->vertical) ? Tk_Width(scrollPtr->tkwin)
  1109.         : Tk_Height(scrollPtr->tkwin);
  1110.     scrollPtr->arrowLength = width - 2*scrollPtr->inset + 1;
  1111.     fieldLength = (scrollPtr->vertical ? Tk_Height(scrollPtr->tkwin)
  1112.         : Tk_Width(scrollPtr->tkwin))
  1113.         - 2*(scrollPtr->arrowLength + scrollPtr->inset);
  1114.     if (fieldLength < 0) {
  1115.     fieldLength = 0;
  1116.     }
  1117.     scrollPtr->sliderFirst = fieldLength*scrollPtr->firstFraction;
  1118.     scrollPtr->sliderLast = fieldLength*scrollPtr->lastFraction;
  1119.  
  1120.     /*
  1121.      * Adjust the slider so that some piece of it is always
  1122.      * displayed in the scrollbar and so that it has at least
  1123.      * a minimal width (so it can be grabbed with the mouse).
  1124.      */
  1125.  
  1126.     if (scrollPtr->sliderFirst > (fieldLength - 2*scrollPtr->borderWidth)) {
  1127.     scrollPtr->sliderFirst = fieldLength - 2*scrollPtr->borderWidth;
  1128.     }
  1129.     if (scrollPtr->sliderFirst < 0) {
  1130.     scrollPtr->sliderFirst = 0;
  1131.     }
  1132.     if (scrollPtr->sliderLast < (scrollPtr->sliderFirst
  1133.         + MIN_SLIDER_LENGTH)) {
  1134.     scrollPtr->sliderLast = scrollPtr->sliderFirst + MIN_SLIDER_LENGTH;
  1135.     }
  1136.     if (scrollPtr->sliderLast > fieldLength) {
  1137.     scrollPtr->sliderLast = fieldLength;
  1138.     }
  1139.     scrollPtr->sliderFirst += scrollPtr->arrowLength + scrollPtr->inset;
  1140.     scrollPtr->sliderLast += scrollPtr->arrowLength + scrollPtr->inset;
  1141.  
  1142.     /*
  1143.      * Register the desired geometry for the window (leave enough space
  1144.      * for the two arrows plus a minimum-size slider, plus border around
  1145.      * the whole window, if any).  Then arrange for the window to be
  1146.      * redisplayed.
  1147.      */
  1148.  
  1149.     if (scrollPtr->vertical) {
  1150.     Tk_GeometryRequest(scrollPtr->tkwin,
  1151.         scrollPtr->width + 2*scrollPtr->inset,
  1152.         2*(scrollPtr->arrowLength + scrollPtr->borderWidth
  1153.         + scrollPtr->inset));
  1154.     } else {
  1155.     Tk_GeometryRequest(scrollPtr->tkwin,
  1156.         2*(scrollPtr->arrowLength + scrollPtr->borderWidth
  1157.         + scrollPtr->inset), scrollPtr->width + 2*scrollPtr->inset);
  1158.     }
  1159.     Tk_SetInternalBorder(scrollPtr->tkwin, scrollPtr->inset);
  1160. }
  1161.  
  1162. /*
  1163.  *--------------------------------------------------------------
  1164.  *
  1165.  * ScrollbarPosition --
  1166.  *
  1167.  *    Determine the scrollbar element corresponding to a
  1168.  *    given position.
  1169.  *
  1170.  * Results:
  1171.  *    One of TOP_ARROW, TOP_GAP, etc., indicating which element
  1172.  *    of the scrollbar covers the position given by (x, y).  If
  1173.  *    (x,y) is outside the scrollbar entirely, then OUTSIDE is
  1174.  *    returned.
  1175.  *
  1176.  * Side effects:
  1177.  *    None.
  1178.  *
  1179.  *--------------------------------------------------------------
  1180.  */
  1181.  
  1182. static int
  1183. ScrollbarPosition(scrollPtr, x, y)
  1184.     register Scrollbar *scrollPtr;    /* Scrollbar widget record. */
  1185.     int x, y;                /* Coordinates within scrollPtr's
  1186.                      * window. */
  1187. {
  1188.     int length, width, tmp;
  1189.  
  1190.     if (scrollPtr->vertical) {
  1191.     length = Tk_Height(scrollPtr->tkwin);
  1192.     width = Tk_Width(scrollPtr->tkwin);
  1193.     } else {
  1194.     tmp = x;
  1195.     x = y;
  1196.     y = tmp;
  1197.     length = Tk_Width(scrollPtr->tkwin);
  1198.     width = Tk_Height(scrollPtr->tkwin);
  1199.     }
  1200.  
  1201.     if ((x < scrollPtr->inset) || (x >= (width - scrollPtr->inset))
  1202.         || (y < scrollPtr->inset) || (y >= (length - scrollPtr->inset))) {
  1203.     return OUTSIDE;
  1204.     }
  1205.  
  1206.     /*
  1207.      * All of the calculations in this procedure mirror those in
  1208.      * DisplayScrollbar.  Be sure to keep the two consistent.
  1209.      */
  1210.  
  1211.     if (y < (scrollPtr->inset + scrollPtr->arrowLength)) {
  1212.     return TOP_ARROW;
  1213.     }
  1214.     if (y < scrollPtr->sliderFirst) {
  1215.     return TOP_GAP;
  1216.     }
  1217.     if (y < scrollPtr->sliderLast) {
  1218.     return SLIDER;
  1219.     }
  1220.     if (y >= (length - (scrollPtr->arrowLength + scrollPtr->inset))) {
  1221.     return BOTTOM_ARROW;
  1222.     }
  1223.     return BOTTOM_GAP;
  1224. }
  1225.  
  1226. /*
  1227.  *--------------------------------------------------------------
  1228.  *
  1229.  * EventuallyRedraw --
  1230.  *
  1231.  *    Arrange for one or more of the fields of a scrollbar
  1232.  *    to be redrawn.
  1233.  *
  1234.  * Results:
  1235.  *    None.
  1236.  *
  1237.  * Side effects:
  1238.  *    None.
  1239.  *
  1240.  *--------------------------------------------------------------
  1241.  */
  1242.  
  1243. static void
  1244. EventuallyRedraw(scrollPtr)
  1245.     register Scrollbar *scrollPtr;    /* Information about widget. */
  1246. {
  1247.     if ((scrollPtr->tkwin == NULL) || (!Tk_IsMapped(scrollPtr->tkwin))) {
  1248.     return;
  1249.     }
  1250.     if ((scrollPtr->flags & REDRAW_PENDING) == 0) {
  1251.     Tcl_DoWhenIdle(DisplayScrollbar, (ClientData) scrollPtr);
  1252.     scrollPtr->flags |= REDRAW_PENDING;
  1253.     }
  1254. }
  1255.